/*
 * Copyright (C) 2009 Google Inc.
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *      http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */
package com.ch_linghu.fanfoudroid.ui.base;

import java.util.ArrayList;
import java.util.Arrays;
import java.util.List;

import android.content.SharedPreferences;
import android.database.Cursor;
import android.os.Bundle;
import android.util.Log;
import android.view.Menu;
import android.view.MotionEvent;
import android.view.View;
import android.widget.ListView;
import android.widget.ProgressBar;
import android.widget.TextView;

import com.ch_linghu.fanfoudroid.R;
import com.ch_linghu.fanfoudroid.TwitterApplication;
import com.ch_linghu.fanfoudroid.app.Preferences;
import com.ch_linghu.fanfoudroid.data.Tweet;
import com.ch_linghu.fanfoudroid.db.StatusTable;
import com.ch_linghu.fanfoudroid.fanfou.IDs;
import com.ch_linghu.fanfoudroid.fanfou.Status;
import com.ch_linghu.fanfoudroid.http.HttpException;
import com.ch_linghu.fanfoudroid.task.GenericTask;
import com.ch_linghu.fanfoudroid.task.TaskAdapter;
import com.ch_linghu.fanfoudroid.task.TaskListener;
import com.ch_linghu.fanfoudroid.task.TaskManager;
import com.ch_linghu.fanfoudroid.task.TaskParams;
import com.ch_linghu.fanfoudroid.task.TaskResult;
import com.ch_linghu.fanfoudroid.ui.module.FlingGestureListener;
import com.ch_linghu.fanfoudroid.ui.module.MyActivityFlipper;
import com.ch_linghu.fanfoudroid.ui.module.SimpleFeedback;
import com.ch_linghu.fanfoudroid.ui.module.TweetAdapter;
import com.ch_linghu.fanfoudroid.ui.module.TweetCursorAdapter;
import com.ch_linghu.fanfoudroid.util.DateTimeHelper;
import com.ch_linghu.fanfoudroid.util.DebugTimer;
import com.hlidskialf.android.hardware.ShakeListener;
import com.markupartist.android.widget.PullToRefreshListView;
import com.markupartist.android.widget.PullToRefreshListView.OnRefreshListener;

/**
 * TwitterCursorBaseLine用于带有静态数据来源（对应数据库的，与twitter表同构的特定表）的展现
 */
public abstract class TwitterCursorBaseActivity extends TwitterListBaseActivity {
	static final String TAG = "TwitterCursorBaseActivity";

	// Views.
	protected PullToRefreshListView mTweetList;
	protected TweetCursorAdapter mTweetAdapter;

	protected View mListHeader;
	protected View mListFooter;

	protected TextView loadMoreBtn;
	protected ProgressBar loadMoreGIF;
	protected TextView loadMoreBtnTop;
	protected ProgressBar loadMoreGIFTop;

	protected static int lastPosition = 0;

	protected ShakeListener mShaker = null;

	// Tasks.
	protected TaskManager taskManager = new TaskManager();
	private GenericTask mRetrieveTask;
	private GenericTask mFollowersRetrieveTask;
	private GenericTask mGetMoreTask;

	private int mRetrieveCount = 0;

	private TaskListener mRetrieveTaskListener = new TaskAdapter() {

		@Override
		public String getName() {
			return "RetrieveTask";
		}

		@Override
		public void onPostExecute(GenericTask task, TaskResult result) {
			// 刷新按钮停止旋转
			loadMoreGIF.setVisibility(View.GONE);
			mTweetList.onRefreshComplete();

			if (result == TaskResult.AUTH_ERROR) {
				mFeedback.failed("登录信息出错");
				logout();
			} else if (result == TaskResult.OK) {
				// TODO: XML处理, GC压力
				SharedPreferences.Editor editor = getPreferences().edit();
				editor.putLong(Preferences.LAST_TWEET_REFRESH_KEY, DateTimeHelper.getNowTime());
				editor.commit();
				// TODO: 1. StatusType(DONE) ;
				if (mRetrieveCount >= StatusTable.MAX_ROW_NUM) {
					// 只有在取回的数据大于MAX时才做GC, 因为小于时可以保证数据的连续性
					getDb().gc(getUserId(), getDatabaseType()); // GC
				}
				draw();
				if (task == mRetrieveTask) {
					goTop();
				}
			} else if (result == TaskResult.IO_ERROR) {
				// FIXME: bad smell
				if (task == mRetrieveTask) {
					mFeedback.failed(((RetrieveTask) task).getErrorMsg());
				} else if (task == mGetMoreTask) {
					mFeedback.failed(((GetMoreTask) task).getErrorMsg());
				}
			} else {
				// do nothing
			}

			// DEBUG
			if (TwitterApplication.DEBUG) {
				DebugTimer.stop();
				Log.v("DEBUG", DebugTimer.getProfileAsString());
			}
		}

		@Override
		public void onPreExecute(GenericTask task) {
			mRetrieveCount = 0;
			mTweetList.prepareForRefresh();

			if (TwitterApplication.DEBUG) {
				DebugTimer.start();
			}
		}

		@Override
		public void onProgressUpdate(GenericTask task, Object param) {
			Log.d(TAG, "onProgressUpdate");
			draw();
		}
	};

	private TaskListener mFollowerRetrieveTaskListener = new TaskAdapter() {

		@Override
		public String getName() {
			return "FollowerRetrieve";
		}

		@Override
		public void onPostExecute(GenericTask task, TaskResult result) {
			if (result == TaskResult.OK) {
				SharedPreferences sp = getPreferences();
				SharedPreferences.Editor editor = sp.edit();
				editor.putLong(Preferences.LAST_FOLLOWERS_REFRESH_KEY, DateTimeHelper.getNowTime());
				editor.commit();
			} else {
				// Do nothing.
			}
		}
	};

	// Refresh data at startup if last refresh was this long ago or greater.
	private static final long REFRESH_THRESHOLD = 5 * 60 * 1000;

	// Refresh followers if last refresh was this long ago or greater.
	private static final long FOLLOWERS_REFRESH_THRESHOLD = 12 * 60 * 60 * 1000;

	abstract protected void markAllRead();

	abstract protected Cursor fetchMessages();

	public abstract int getDatabaseType();

	public abstract String getUserId();

	public abstract String fetchMaxId();

	public abstract String fetchMinId();

	public abstract int addMessages(ArrayList<Tweet> tweets, boolean isUnread);

	public abstract List<Status> getMessageSinceId(String maxId) throws HttpException;

	public abstract List<Status> getMoreMessageFromId(String minId) throws HttpException;

	public static final int CONTEXT_REPLY_ID = Menu.FIRST + 1;
	// public static final int CONTEXT_AT_ID = Menu.FIRST + 2;
	public static final int CONTEXT_RETWEET_ID = Menu.FIRST + 3;
	public static final int CONTEXT_DM_ID = Menu.FIRST + 4;
	public static final int CONTEXT_MORE_ID = Menu.FIRST + 5;
	public static final int CONTEXT_ADD_FAV_ID = Menu.FIRST + 6;
	public static final int CONTEXT_DEL_FAV_ID = Menu.FIRST + 7;

	@Override
	protected void setupState() {
		Cursor cursor;

		cursor = fetchMessages(); // getDb().fetchMentions();
		setTitle(getActivityTitle());
		startManagingCursor(cursor);

		mTweetList = (PullToRefreshListView) findViewById(R.id.tweet_list);

		// TODO: 需处理没有数据时的情况
		Log.d("LDS", cursor.getCount() + " cursor count");
		setupListHeader(true);

		mTweetAdapter = new TweetCursorAdapter(this, cursor);
		mTweetList.setAdapter(mTweetAdapter);
		// ? registerOnClickListener(mTweetList);

	}

	/**
	 * 绑定listView底部 - 载入更多 NOTE: 必须在listView#setAdapter之前调用
	 */
	protected void setupListHeader(boolean addFooter) {
		// Add Header to ListView
		// mListHeader = View.inflate(this, R.layout.listview_header, null);
		// mTweetList.addHeaderView(mListHeader, null, true);
		mTweetList.setOnRefreshListener(new OnRefreshListener() {
			@Override
			public void onRefresh() {
				doRetrieve();
			}
		});

		// Add Footer to ListView
		mListFooter = View.inflate(this, R.layout.listview_footer, null);
		mTweetList.addFooterView(mListFooter, null, true);

		// Find View
		loadMoreBtn = (TextView) findViewById(R.id.ask_for_more);
		loadMoreGIF = (ProgressBar) findViewById(R.id.rectangleProgressBar);
		loadMoreBtnTop = (TextView) findViewById(R.id.ask_for_more_header);
		loadMoreGIFTop = (ProgressBar) findViewById(R.id.rectangleProgressBar_header);

	}

	@Override
	protected void specialItemClicked(int position) {
		// 注意 mTweetAdapter.getCount 和 mTweetList.getCount的区别
		// 前者仅包含数据的数量（不包括foot和head），后者包含foot和head
		// 因此在同时存在foot和head的情况下，list.count = adapter.count + 2
		if (position == 0) {
			// 第一个Item(header)
			loadMoreGIFTop.setVisibility(View.VISIBLE);
			doRetrieve();
		} else if (position == mTweetList.getCount() - 1) {
			// 最后一个Item(footer)
			loadMoreGIF.setVisibility(View.VISIBLE);
			doGetMore();
		}
	}

	@Override
	protected int getLayoutId() {
		return R.layout.main;
	}

	@Override
	protected ListView getTweetList() {
		return mTweetList;
	}

	@Override
	protected TweetAdapter getTweetAdapter() {
		return mTweetAdapter;
	}

	@Override
	protected boolean useBasicMenu() {
		return true;
	}

	@Override
	protected Tweet getContextItemTweet(int position) {
		position = position - 1;
		// 因为List加了Header和footer，所以要跳过第一个以及忽略最后一个
		if (position >= 0 && position < mTweetAdapter.getCount()) {
			Cursor cursor = (Cursor) mTweetAdapter.getItem(position);
			if (cursor == null) {
				return null;
			} else {
				return StatusTable.parseCursor(cursor);
			}
		} else {
			return null;
		}
	}

	@Override
	protected void updateTweet(Tweet tweet) {
		// TODO: updateTweet() 在哪里调用的? 目前尚只支持:
		// updateTweet(String tweetId, ContentValues values)
		// setFavorited(String tweetId, String isFavorited)
		// 看是否还需要增加updateTweet(Tweet tweet)方法

		// 对所有相关表的对应消息都进行刷新（如果存在的话）
		// getDb().updateTweet(TwitterDbAdapter.TABLE_FAVORITE, tweet);
		// getDb().updateTweet(TwitterDbAdapter.TABLE_MENTION, tweet);
		// getDb().updateTweet(TwitterDbAdapter.TABLE_TWEET, tweet);
	}

	@Override
	protected boolean _onCreate(Bundle savedInstanceState) {
		Log.d(TAG, "onCreate.");
		if (super._onCreate(savedInstanceState)) {
			// Mark all as read.
			// getDb().markAllMentionsRead();
			markAllRead();

			boolean shouldRetrieve = false;

			// FIXME： 该子类页面全部使用了这个统一的计时器，导致进入Mention等分页面后经常不会自动刷新
			long lastRefreshTime = mPreferences.getLong(Preferences.LAST_TWEET_REFRESH_KEY, 0);
			long nowTime = DateTimeHelper.getNowTime();

			long diff = nowTime - lastRefreshTime;
			Log.d(TAG, "Last refresh was " + diff + " ms ago.");

			if (diff > REFRESH_THRESHOLD) {
				shouldRetrieve = true;
			} else if (isTrue(savedInstanceState, SIS_RUNNING_KEY)) {
				// Check to see if it was running a send or retrieve task.
				// It makes no sense to resend the send request (don't want
				// dupes)
				// so we instead retrieve (refresh) to see if the message has
				// posted.
				Log.d(TAG, "Was last running a retrieve or send task. Let's refresh.");
				shouldRetrieve = true;
			}

			if (shouldRetrieve) {
				doRetrieve();
			}

			goTop(); // skip the header

			long lastFollowersRefreshTime = mPreferences.getLong(Preferences.LAST_FOLLOWERS_REFRESH_KEY, 0);

			diff = nowTime - lastFollowersRefreshTime;
			Log.d(TAG, "Last followers refresh was " + diff + " ms ago.");

			// FIXME: 目前还没有对Followers列表做逻辑处理，因此暂时去除对Followers的获取。
			// 未来需要实现@用户提示时，对Follower操作需要做一次review和refactoring
			// 现在频繁会出现主键冲突的问题。
			//
			// Should Refresh Followers
			// if (diff > FOLLOWERS_REFRESH_THRESHOLD
			// && (mRetrieveTask == null || mRetrieveTask.getStatus() !=
			// GenericTask.Status.RUNNING)) {
			// Log.d(TAG, "Refresh followers.");
			// doRetrieveFollowers();
			// }

			// 手势识别
			registerGestureListener();

			// 晃动刷新
			registerShakeListener();

			return true;
		} else {
			return false;
		}

	}

	@Override
	protected void onResume() {
		Log.d(TAG, "onResume.");
		if (lastPosition != 0) {
			mTweetList.setSelection(lastPosition);
		}
		if (mShaker != null) {
			mShaker.resume();
		}
		super.onResume();
		checkIsLogedIn();
	}

	@Override
	protected void onSaveInstanceState(Bundle outState) {
		super.onSaveInstanceState(outState);

		if (mRetrieveTask != null && mRetrieveTask.getStatus() == GenericTask.Status.RUNNING) {
			outState.putBoolean(SIS_RUNNING_KEY, true);
		}
	}

	@Override
	protected void onRestoreInstanceState(Bundle bundle) {
		super.onRestoreInstanceState(bundle);
		// mTweetEdit.updateCharsRemain();
	}

	@Override
	protected void onDestroy() {
		Log.d(TAG, "onDestroy.");
		super.onDestroy();

		taskManager.cancelAll();

		// 刷新按钮停止旋转
		if (loadMoreGIF != null) {
			loadMoreGIF.setVisibility(View.GONE);
		}
		if (mTweetList != null) {
			mTweetList.onRefreshComplete();
		}
	}

	@Override
	protected void onPause() {
		Log.d(TAG, "onPause.");
		if (mShaker != null) {
			mShaker.pause();
		}
		super.onPause();
		lastPosition = mTweetList.getFirstVisiblePosition();
	}

	@Override
	protected void onRestart() {
		Log.d(TAG, "onRestart.");
		super.onRestart();
	}

	@Override
	protected void onStart() {
		Log.d(TAG, "onStart.");
		super.onStart();
	}

	@Override
	protected void onStop() {
		Log.d(TAG, "onStop.");
		super.onStop();
	}

	// UI helpers.

	@Override
	protected String getActivityTitle() {
		return null;
	}

	@Override
	protected void adapterRefresh() {
		mTweetAdapter.notifyDataSetChanged();
		mTweetAdapter.refresh();
	}

	// Retrieve interface
	public void updateProgress(String progress) {
		mProgressText.setText(progress);
	}

	public void draw() {
		mTweetAdapter.refresh();
	}

	public void goTop() {
		Log.d(TAG, "goTop.");
		mTweetList.setSelection(1);
	}

	private void doRetrieveFollowers() {
		Log.d(TAG, "Attempting followers retrieve.");

		if (mFollowersRetrieveTask != null && mFollowersRetrieveTask.getStatus() == GenericTask.Status.RUNNING) {
			return;
		} else {
			mFollowersRetrieveTask = new FollowersRetrieveTask();
			mFollowersRetrieveTask.setListener(mFollowerRetrieveTaskListener);
			mFollowersRetrieveTask.execute();

			taskManager.addTask(mFollowersRetrieveTask);
			// Don't need to cancel FollowersTask (assuming it ends properly).
			mFollowersRetrieveTask.setCancelable(false);
		}
	}

	public void doRetrieve() {
		Log.d(TAG, "Attempting retrieve.");

		if (mRetrieveTask != null && mRetrieveTask.getStatus() == GenericTask.Status.RUNNING) {
			return;
		} else {
			mRetrieveTask = new RetrieveTask();
			mRetrieveTask.setFeedback(mFeedback);
			mRetrieveTask.setListener(mRetrieveTaskListener);
			mRetrieveTask.execute();

			// Add Task to manager
			taskManager.addTask(mRetrieveTask);
		}
	}

	private class RetrieveTask extends GenericTask {
		private String _errorMsg;

		public String getErrorMsg() {
			return _errorMsg;
		}

		@Override
		protected TaskResult _doInBackground(TaskParams... params) {
			List<com.ch_linghu.fanfoudroid.fanfou.Status> statusList;

			try {
				String maxId = fetchMaxId(); // getDb().fetchMaxMentionId();
				statusList = getMessageSinceId(maxId);
			} catch (HttpException e) {
				Log.e(TAG, e.getMessage(), e);
				_errorMsg = e.getMessage();
				return TaskResult.IO_ERROR;
			}

			ArrayList<Tweet> tweets = new ArrayList<Tweet>();
			for (com.ch_linghu.fanfoudroid.fanfou.Status status : statusList) {
				if (isCancelled()) {
					return TaskResult.CANCELLED;
				}
				tweets.add(Tweet.create(status));
				if (isCancelled()) {
					return TaskResult.CANCELLED;
				}
			}

			publishProgress(SimpleFeedback.calProgressBySize(40, 20, tweets));
			mRetrieveCount = addMessages(tweets, false);

			return TaskResult.OK;
		}

	}

	private class FollowersRetrieveTask extends GenericTask {

		@Override
		protected TaskResult _doInBackground(TaskParams... params) {
			try {
				// TODO: 目前仅做新API兼容性改动，待完善Follower处理
				IDs followers = getApi().getFollowersIDs();
				List<String> followerIds = Arrays.asList(followers.getIDs());
				getDb().syncFollowers(followerIds);
			} catch (HttpException e) {
				Log.e(TAG, e.getMessage(), e);
				return TaskResult.IO_ERROR;
			}
			return TaskResult.OK;
		}
	}

	// GET MORE TASK

	private class GetMoreTask extends GenericTask {
		private String _errorMsg;

		public String getErrorMsg() {
			return _errorMsg;
		}

		@Override
		protected TaskResult _doInBackground(TaskParams... params) {
			List<com.ch_linghu.fanfoudroid.fanfou.Status> statusList;

			String minId = fetchMinId(); // getDb().fetchMaxMentionId();

			if (minId == null) {
				return TaskResult.FAILED;
			}

			try {
				statusList = getMoreMessageFromId(minId);
			} catch (HttpException e) {
				Log.e(TAG, e.getMessage(), e);
				_errorMsg = e.getMessage();
				return TaskResult.IO_ERROR;
			}

			if (statusList == null) {
				return TaskResult.FAILED;
			}

			ArrayList<Tweet> tweets = new ArrayList<Tweet>();
			publishProgress(SimpleFeedback.calProgressBySize(40, 20, tweets));

			for (com.ch_linghu.fanfoudroid.fanfou.Status status : statusList) {
				if (isCancelled()) {
					return TaskResult.CANCELLED;
				}

				tweets.add(Tweet.create(status));

				if (isCancelled()) {
					return TaskResult.CANCELLED;
				}
			}

			addMessages(tweets, false); // getDb().addMentions(tweets, false);

			return TaskResult.OK;
		}
	}

	public void doGetMore() {
		Log.d(TAG, "Attempting getMore.");

		if (mGetMoreTask != null && mGetMoreTask.getStatus() == GenericTask.Status.RUNNING) {
			return;
		} else {
			mGetMoreTask = new GetMoreTask();
			mGetMoreTask.setFeedback(mFeedback);
			mGetMoreTask.setListener(mRetrieveTaskListener);
			mGetMoreTask.execute();

			// Add Task to manager
			taskManager.addTask(mGetMoreTask);
		}
	}

	// ////////////////// Gesture test /////////////////////////////////////
	private static boolean useGestrue;
	{
		useGestrue = TwitterApplication.mPref.getBoolean(Preferences.USE_GESTRUE, false);
		if (useGestrue) {
			Log.v(TAG, "Using Gestrue!");
		} else {
			Log.v(TAG, "Not Using Gestrue!");
		}
	}

	// ////////////////// Gesture test /////////////////////////////////////
	private static boolean useShake;
	{
		useShake = TwitterApplication.mPref.getBoolean(Preferences.USE_SHAKE, false);
		if (useShake) {
			Log.v(TAG, "Using Shake to refresh!");
		} else {
			Log.v(TAG, "Not Using Shake!");
		}
	}

	protected FlingGestureListener myGestureListener = null;

	@Override
	public boolean onTouchEvent(MotionEvent event) {
		if (useGestrue && myGestureListener != null) {
			return myGestureListener.getDetector().onTouchEvent(event);
		}
		return super.onTouchEvent(event);
	}

	// use it in _onCreate
	private void registerGestureListener() {
		if (useGestrue) {
			myGestureListener = new FlingGestureListener(this, MyActivityFlipper.create(this));
			getTweetList().setOnTouchListener(myGestureListener);
		}
	}

	// use it in _onCreate
	private void registerShakeListener() {
		if (useShake) {
			mShaker = new ShakeListener(this);
			mShaker.setOnShakeListener(new ShakeListener.OnShakeListener() {

				@Override
				public void onShake() {
					Log.v(TAG, "onShake");
					doRetrieve();
				}
			});
		}
	}
}